#include <Arduino.h>
#include "WiFi.h"
#include "PubSubClient.h"
#include "Ticker.h"
#include "esp_http_server.h"
#include "esp_timer.h"
#include "esp_camera.h"
#include "img_converters.h"
#include "Arduino.h"
#include "sdkconfig.h"
#include "HardwareSerial.h"
#include <ArduinoOTA.h>
#include <WebServer.h>
#include <WiFiClient.h>


/////////////////////////////////////////////引脚初始化//////////////////////////////////////////
extern int DRV_A;
extern int DRV_B;
extern int DIR_A;
extern int DIR_B;
extern int ledPin;
extern int buzzerPin;
extern int servoPin;
extern int ledVal;

extern std::string ipadd1 = "";

HardwareSerial MySerial_stm32(1); 
///////////////////////////////////////////重要地址////////////////////////////////////////////////

 
const char *ssid = "VODKA";               //wifi名 //hh     VODKA
const char *password = "000999111";       //wifi密码 //hh000555
const char *mqtt_server = "175.178.28.183"; // IP地址
const int port = 1883;                     //端口号

////////////////////////////////////////////自定义话题////////////////////////////////////////////
const char TEMP_TOPIC[]   = "temp";  //温度topic
const char HUMI_TOPIC[]     = "humi";   //湿度topic
const char SMOKE_TOPIC[]    = "smoke";   //烟雾topic

const char APP_TOPIC[]    = "APP";   //要订阅app发布的tpoic
const char LOCAL_TOPIC[]  = "local"; //发布本地控制模块状态topic





/*
#define mqtt_devid "ESP32" //设备ID ESP32
#define mqtt_pubid "537981"        //产品ID
//鉴权信息
#define mqtt_password "version=2018-10-31&res=products%2F537981%2Fdevices%2FESP32&et=4090222702&method=md5&sign=m7pye%2FPnWUoiHwgimKXSOA%3D%3D" //鉴权信息
*/



#define mqtt_devid "ESP32_2" //设备ID ESP32
#define mqtt_pubid "537981"        //产品ID
//鉴权信息
#define mqtt_password "version=2018-10-31&res=products%2F537981%2Fdevices%2FESP32_2&et=4090807062&method=md5&sign=vjBKfz%2BDbTkhbfnhhSZt5Q%3D%3D" //鉴权信息


/*
#define mqtt_devid "ESP3" //设备ID ESP32
#define mqtt_pubid "537981"        //产品ID
//鉴权信息
#define mqtt_password "version=2018-10-31&res=products%2F537981%2Fdevices%2FESP3&et=4090825062&method=md5&sign=EJG%2FFbk0GPBji0d0RFW0mg%3D%3D" //鉴权信息
*/






/* define CAMERA_MODEL_AI_THINKER */
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22
//////ESP32核心//////
#define APP_CPU 1
#define PRO_CPU 0



#define CAMERA_MODEL_AI_THINKER // Has PSRAM 相机模式
#include "camera_pins.h" //相机引脚索引文件
#include "OV2640.h"
#include "home_wifi_multi.h"
#include <esp_bt.h>
#include <esp_wifi.h>
#include <esp_sleep.h>
#include <driver/rtc_io.h>






String translateEncryptionType(wifi_auth_mode_t encryptionType) {   //获取wifi的特性
  switch (encryptionType){
    case (WIFI_AUTH_OPEN):
      return "Open";
    case (WIFI_AUTH_WEP):
      return "WEP";
    case (WIFI_AUTH_WPA_PSK):
      return "WPA_PSK";
    case (WIFI_AUTH_WPA2_PSK):
      return "WPA2_PSK";
    case (WIFI_AUTH_WPA_WPA2_PSK):
      return "WPA_WPA2_PSK";
    case (WIFI_AUTH_WPA2_ENTERPRISE):
      return "WPA2_ENTERPRISE";
  }
} 


void scanNetworks(){    //搜索附近的WIFI源并打印出来
  int numberOfNetworks= WiFi.scanNetworks();
  //Serial.print("Number of networks found: ");
  //Serial.println(numberOfNetworks);
  for (int i = 0; i< numberOfNetworks; i++) {
    //Serial.print("Networkname: ");
    //Serial.println(WiFi.SSID(i));
    //Serial.print("Signalstrength: ");
    //Serial.println(WiFi.RSSI(i));
    //Serial.print("MACaddress: ");
    //Serial.println(WiFi.BSSIDstr(i));
    //Serial.print("Encryptiontype: ");
    String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
    //Serial.println(encryptionTypeDescription);
    //Serial.println("-----------------------");
    }
  }






///////////////////////////////////////////////////////////ESP32图传部分，含内网穿透//////////////////////////////////////////////
OV2640 cam;

WebServer server(80);

// ===== rtos task handles =========================
// Streaming is implemented with 3 tasks:
TaskHandle_t tMjpeg;   // handles client connections to the webserver
TaskHandle_t tCam;     // handles getting picture frames from the camera and storing them locally
TaskHandle_t tStream;  // actually streaming frames to all connected clients

// frameSync semaphore is used to prevent streaming buffer as it is replaced with the next frame
SemaphoreHandle_t frameSync = NULL;

// Queue stores currently connected clients to whom we are streaming
QueueHandle_t streamingClients;

///////刷新帧率//////
const int FPS = 10;

// We will handle web client requests every 50 ms (20 Hz)
const int WSINTERVAL = 100;

// ======== Server Connection Handler Task ==========================
void mjpegCB(void* pvParameters) {
  TickType_t xLastWakeTime;
  const TickType_t xFrequency = pdMS_TO_TICKS(WSINTERVAL);

  // Creating frame synchronization semaphore and initializing it
  frameSync = xSemaphoreCreateBinary();
  xSemaphoreGive( frameSync );

  // Creating a queue to track all connected clients
  streamingClients = xQueueCreate( 10, sizeof(WiFiClient*) );

  //=== setup section  ==================

  //  Creating RTOS task for grabbing frames from the camera
  xTaskCreatePinnedToCore(
    camCB,        // callback
    "cam",        // name
    4096,         // stacj size
    NULL,         // parameters
    2,            // priority
    &tCam,        // RTOS task handle
    APP_CPU);     // core

  //  Creating task to push the stream to all connected clients
  xTaskCreatePinnedToCore(
    streamCB,
    "strmCB",
    4 * 1024,
    NULL, //(void*) handler,
    2,
    &tStream,
    APP_CPU);

  //  Registering webserver handling routines
  server.on("/mjpeg/1", HTTP_GET, handleJPGSstream);
  server.on("/jpg", HTTP_GET, handleJPG);
  server.onNotFound(handleNotFound);

  //  Starting webserver
  server.begin();

  //=== loop() section  ===================
  xLastWakeTime = xTaskGetTickCount();
  for (;;) {
    server.handleClient();

    //  After every server client handling request, we let other tasks run and then pause
    taskYIELD();
    vTaskDelayUntil(&xLastWakeTime, xFrequency);
  }
}


// Commonly used variables:
volatile size_t camSize;    // size of the current frame, byte
volatile char* camBuf;      // pointer to the current frame


// ==== RTOS task to grab frames from the camera =========================
void camCB(void* pvParameters) {

  TickType_t xLastWakeTime;

  //  A running interval associated with currently desired frame rate
  const TickType_t xFrequency = pdMS_TO_TICKS(1000 / FPS);

  // Mutex for the critical section of swithing the active frames around
  portMUX_TYPE xSemaphore = portMUX_INITIALIZER_UNLOCKED;

  //  Pointers to the 2 frames, their respective sizes and index of the current frame
  char* fbs[2] = { NULL, NULL };
  size_t fSize[2] = { 0, 0 };
  int ifb = 0;

  //=== loop() section  ===================
  xLastWakeTime = xTaskGetTickCount();

  for (;;) {

    //  Grab a frame from the camera and query its size
    cam.run();
    size_t s = cam.getSize();

    //  If frame size is more that we have previously allocated - request  125% of the current frame space
    if (s > fSize[ifb]) {
      fSize[ifb] = s * 4 / 3;
      fbs[ifb] = allocateMemory(fbs[ifb], fSize[ifb]);
    }

    //  Copy current frame into local buffer
    char* b = (char*) cam.getfb();
    memcpy(fbs[ifb], b, s);

    //  Let other tasks run and wait until the end of the current frame rate interval (if any time left)
    taskYIELD();
    vTaskDelayUntil(&xLastWakeTime, xFrequency);

    //  Only switch frames around if no frame is currently being streamed to a client
    //  Wait on a semaphore until client operation completes
    xSemaphoreTake( frameSync, portMAX_DELAY );

    //  Do not allow interrupts while switching the current frame
    portENTER_CRITICAL(&xSemaphore);
    camBuf = fbs[ifb];
    camSize = s;
    ifb++;
    ifb &= 1;  // this should produce 1, 0, 1, 0, 1 ... sequence
    portEXIT_CRITICAL(&xSemaphore);

    //  Let anyone waiting for a frame know that the frame is ready
    xSemaphoreGive( frameSync );

    //  Technically only needed once: let the streaming task know that we have at least one frame
    //  and it could start sending frames to the clients, if any
    xTaskNotifyGive( tStream );

    //  Immediately let other (streaming) tasks run
    taskYIELD();

    //  If streaming task has suspended itself (no active clients to stream to)
    //  there is no need to grab frames from the camera. We can save some juice
    //  by suspedning the tasks
    if ( eTaskGetState( tStream ) == eSuspended ) {
      vTaskSuspend(NULL);  // passing NULL means "suspend yourself"
    }
  }
}


// ==== Memory allocator that takes advantage of PSRAM if present =======================
char* allocateMemory(char* aPtr, size_t aSize) {

  //  Since current buffer is too smal, free it
  if (aPtr != NULL) free(aPtr);


  size_t freeHeap = ESP.getFreeHeap();
  char* ptr = NULL;

  // If memory requested is more than 2/3 of the currently free heap, try PSRAM immediately
  if ( aSize > freeHeap * 2 / 3 ) {
    if ( psramFound() && ESP.getFreePsram() > aSize ) {
      ptr = (char*) ps_malloc(aSize);
    }
  }
  else {
    //  Enough free heap - let's try allocating fast RAM as a buffer
    ptr = (char*) malloc(aSize);

    //  If allocation on the heap failed, let's give PSRAM one more chance:
    if ( ptr == NULL && psramFound() && ESP.getFreePsram() > aSize) {
      ptr = (char*) ps_malloc(aSize);
    }
  }

  // Finally, if the memory pointer is NULL, we were not able to allocate any memory, and that is a terminal condition.
  if (ptr == NULL) {
    ESP.restart();
  }
  return ptr;
}


// ==== STREAMING ======================================================
const char HEADER[] = "HTTP/1.1 200 OK\r\n" \
                      "Access-Control-Allow-Origin: *\r\n" \
                      "Content-Type: multipart/x-mixed-replace; boundary=123456789000000000000987654321\r\n";
const char BOUNDARY[] = "\r\n--123456789000000000000987654321\r\n";
const char CTNTTYPE[] = "Content-Type: image/jpeg\r\nContent-Length: ";
const int hdrLen = strlen(HEADER);
const int bdrLen = strlen(BOUNDARY);
const int cntLen = strlen(CTNTTYPE);


// ==== Handle connection request from clients ===============================
void handleJPGSstream(void)
{
  //  Can only acommodate 10 clients. The limit is a default for WiFi connections
  if ( !uxQueueSpacesAvailable(streamingClients) ) return;


  //  Create a new WiFi Client object to keep track of this one
  WiFiClient* client = new WiFiClient();
  *client = server.client();

  //  Immediately send this client a header
  client->write(HEADER, hdrLen);
  client->write(BOUNDARY, bdrLen);

  // Push the client to the streaming queue
  xQueueSend(streamingClients, (void *) &client, 0);

  // Wake up streaming tasks, if they were previously suspended:
  if ( eTaskGetState( tCam ) == eSuspended ) vTaskResume( tCam );
  if ( eTaskGetState( tStream ) == eSuspended ) vTaskResume( tStream );
}


// ==== Actually stream content to all connected clients ========================
void streamCB(void * pvParameters) {
  char buf[16];
  TickType_t xLastWakeTime;
  TickType_t xFrequency;

  //  Wait until the first frame is captured and there is something to send
  //  to clients
  ulTaskNotifyTake( pdTRUE,          /* Clear the notification value before exiting. */
                    portMAX_DELAY ); /* Block indefinitely. */

  xLastWakeTime = xTaskGetTickCount();
  for (;;) {
    // Default assumption we are running according to the FPS
    xFrequency = pdMS_TO_TICKS(1000 / FPS);

    //  Only bother to send anything if there is someone watching
    UBaseType_t activeClients = uxQueueMessagesWaiting(streamingClients);
    if ( activeClients ) {
      // Adjust the period to the number of connected clients
      xFrequency /= activeClients;

      //  Since we are sending the same frame to everyone,
      //  pop a client from the the front of the queue
      WiFiClient *client;
      xQueueReceive (streamingClients, (void*) &client, 0);

      //  Check if this client is still connected.

      if (!client->connected()) {
        //  delete this client reference if s/he has disconnected
        //  and don't put it back on the queue anymore. Bye!
        delete client;
      }
      else {

        //  Ok. This is an actively connected client.
        //  Let's grab a semaphore to prevent frame changes while we
        //  are serving this frame
        xSemaphoreTake( frameSync, portMAX_DELAY );

        client->write(CTNTTYPE, cntLen);
        sprintf(buf, "%d\r\n\r\n", camSize);
        client->write(buf, strlen(buf));
        client->write((char*) camBuf, (size_t)camSize);
        client->write(BOUNDARY, bdrLen);

        // Since this client is still connected, push it to the end
        // of the queue for further processing
        xQueueSend(streamingClients, (void *) &client, 0);

        //  The frame has been served. Release the semaphore and let other tasks run.
        //  If there is a frame switch ready, it will happen now in between frames
        xSemaphoreGive( frameSync );
        taskYIELD();
      }
    }
    else {
      //  Since there are no connected clients, there is no reason to waste battery running
      vTaskSuspend(NULL);
    }
    //  Let other tasks run after serving every client
    taskYIELD();
    vTaskDelayUntil(&xLastWakeTime, xFrequency);
  }
}



const char JHEADER[] = "HTTP/1.1 200 OK\r\n" \
                       "Content-disposition: inline; filename=capture.jpg\r\n" \
                       "Content-type: image/jpeg\r\n\r\n";
const int jhdLen = strlen(JHEADER);

// ==== Serve up one JPEG frame =============================================
void handleJPG(void)
{
  WiFiClient client = server.client();

  if (!client.connected()) return;
  cam.run();
  client.write(JHEADER, jhdLen);
  client.write((char*)cam.getfb(), cam.getSize());
}


// ==== Handle invalid URL requests ============================================
void handleNotFound()
{
  String message = "Server is running!\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  server.send(200, "text / plain", message);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////引脚与http定义/////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct {
        httpd_req_t *req;
        size_t len;
} jpg_chunking_t;

/* Defining motor and servo pins */
extern int DRV_A = 12;
extern int DRV_B = 13;
extern int DIR_A = 14;
extern int DIR_B = 15;

extern int ledVal = 20;  // setting bright of flash LED 0-255

extern int ledPin = 4;  // set digital pin GPIO4 as LED pin (use biult-in LED)
extern int buzzerPin = 2;  // set digital pin GPIO2 as LED pin (use Active Buzzer)
extern int servoPin = 2;  // set digital pin GPIO2 as servo pin (use SG90)

//String getTopic = "";
//String getMsg = "";





unsigned long previousMillis = 0;

void startCameraServer();

void initServo() {
  ledcSetup(8, 50, 16); /*50 hz PWM, 16-bit resolution and range from 3250 to 6500 */
  ledcAttachPin(servoPin, 8);
}

void initLed() {
  ledcSetup(7, 5000, 8); /* 5000 hz PWM, 8-bit resolution and range from 0 to 255 */
  ledcAttachPin(ledPin, 7);
}

void init_moter(){
  ledcSetup(9,50,8);
  ledcAttachPin(DRV_A, 9);
  ledcSetup(10,50,8);
  ledcAttachPin(DRV_B, 10);
  ledcSetup(11,50,8);
  ledcAttachPin(DIR_A, 11);
  ledcSetup(12,50,8);
  ledcAttachPin(DIR_B, 12);
}













//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


 
WiFiClient espClient;           //创建一个WIFI连接客户端
PubSubClient client(espClient); // 创建一个PubSub客户端, 传入创建的WIFI客户端
 
 
char msgJson[75]; //发送信息缓冲区
//信息模板
char dataTemplate[] = "{\"摄氏度：36.0\",\"相对湿度：50.0\"}\r\n";   //"{\"id\":123,\"dp\":{\"temp\":[{\"v\":%.2f}],\"humi\":[{\"v\":%.2f}]}}";
Ticker tim1; //定时器,用来循环上传数据
 
 
//连接WIFI相关函数
void setupWifi()
{

  scanNetworks();
  delay(1000);
  //Serial.println("连接wifi");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  while (!WiFi.isConnected())
  { 
    delay(1500);
    //Serial.print(".");
    
  }
  //Serial.println("ok");
  //Serial.println("wifi连接成功");
}
 
 

 
 
//向主题发送模拟的温湿度数据
void sendTempAndHumi()
{
  if (client.connected())
  {
     snprintf(msgJson, 75, dataTemplate, 36.0, 50.0); //将模拟温湿度数据套入dataTemplate模板中, 生成的字符串传给msgJson
 
 
    //Serial.print("public the data:");
    //Serial.println(msgJson);
    client.publish("ESP32", (uint8_t *)msgJson, strlen(msgJson)); 
    //发送数据到主题
  }
}
 
 
//重连函数, 如果客户端断线,可以通过此函数重连
void clientReconnect()
{
  while (!client.connected()) //再重连客户端
  {
    Serial.println("reconnect mqtt...");
    if (client.connect(mqtt_devid, mqtt_pubid, mqtt_password))
    {
      Serial.println("connected");
      client.subscribe("APP"); //订阅命令下发主题
    }
    else
    {
      Serial.println("failed");
      Serial.println(client.state());
      Serial.println("try again in 5 sec");
      delay(5000);
    }
  }
}


/*
///////////////////////////////////////IP地址数据类型转换，发送至MQTT的前期工作/////////////////////////////////////////////////

void parseBytes(const char* str, char sep, byte* bytes, int maxBytes, int base) { 
    for (int i = 0; i < maxBytes; i++) { 
     bytes[i] = strtoul(str, NULL, base); // Convert byte 
     str = strchr(str, sep);    // Find next separator 
     if (str == NULL || *str == '\0') { 
      break;       // No more separators, exit 
     } 
     str++;        // Point to next character after separator 
    } 
} 

*/



////////////////////////////////////////////////命令动作实际定义///////////////////////////////////////////////////////////

//开灯
void TurnOnLed(){
  //Serial.println("\r\nLED on");
  ledcWrite(7, ledVal);//digitalWrite(ledPin,LOW);
}


//关灯
void TurnOffLed(){
  //Serial.println("\r\nLED off");
  ledcWrite(7, 0);//digitalWrite(ledPin,HIGH);
}
 




//前行
void Move_F(){
  //Serial.println("\r\nMove forward");
  // digitalWrite(DRV_A, LOW);
  // digitalWrite(DRV_B, HIGH);
  // digitalWrite(DIR_A, LOW);
  // digitalWrite(DIR_B, HIGH);
  ledcWrite(9, 0);
  ledcWrite(10, 255);
  ledcWrite(11, 0);
  ledcWrite(12, 255);
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}

//后退
void Move_B(){
  //Serial.println("\r\nMove backward");
  // digitalWrite(DRV_A, HIGH);
  // digitalWrite(DRV_B, LOW);
  // digitalWrite(DIR_A, HIGH);
  // digitalWrite(DIR_B, LOW);
  ledcWrite(9, 255);
  ledcWrite(10, 0);
  ledcWrite(11, 255);
  ledcWrite(12, 0);
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}

//左转
void Move_L(){
  //Serial.println("\r\nMove left");
  // digitalWrite(DRV_A, LOW);
  // digitalWrite(DRV_B, HIGH);
  // digitalWrite(DIR_A, HIGH);
  // digitalWrite(DIR_B, LOW);
  ledcWrite(9, 0);
  ledcWrite(10, 80);
  ledcWrite(11, 80);
  ledcWrite(12, 0);
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}

//右转
void Move_R(){
  //Serial.println("\r\nMove right");
  // digitalWrite(DRV_A, HIGH);
  // digitalWrite(DRV_B, LOW);
  // digitalWrite(DIR_A, LOW);
  // digitalWrite(DIR_B, HIGH);
  ledcWrite(9, 80);
  ledcWrite(10, 0);
  ledcWrite(11, 0);
  ledcWrite(12, 80);
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}

//停止运动
void Move_S(){
  //Serial.println("\r\nStop moving");
  //digitalWrite(DRV_A, LOW);
  //digitalWrite(DRV_B, LOW);
  //digitalWrite(DIR_A, LOW);
  //digitalWrite(DIR_B, LOW);
  ledcWrite(9, 0);
  ledcWrite(10, 0);
  ledcWrite(11, 0);
  ledcWrite(12, 0);
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}

//模式一
void Mode_ONE(){
  //Serial.println("N");
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}
//模式二
void Mode_TWO(){
  //Serial.println("Z");
  //httpd_resp_set_type(req, "text/html");
  //return httpd_resp_send(req, "OK", 2);
}







//收到主题下发的回调, 注意这个回调要实现三个形参 1:topic 主题, 2: payload: 传递过来的信息 3: length: 长度
void callback(char *topic, byte *payload, unsigned int length) 
{
    //Serial.print("Message arrived in topic: ");
    Serial.println(topic);
    //Serial.print("Message:");
    
    String Message;
    
    for (int i = 0; i < length; i++) {
        Message = Message + (char) payload[i];  // convert *byte to string
    }
    Serial.print(Message);
    
    
    char cmd[32] = {0,};
    
    if (Message == "E") { TurnOnLed(); }   // LED on
    if (Message == "e") { TurnOffLed(); } // LED off
    if (Message == "F") { Move_F(); }  // Move forward
    if (Message == "B") { Move_B(); } // Move backward
    if (Message == "L") { Move_L() ;}   // Move left
    if (Message == "R") { Move_R() ;} // Move right
    if (Message == "S") { Move_S();} // Stop moving
    if (Message == "N") { Mode_ONE() ;} // 
    if (Message == "Z") { Mode_TWO() ;} //
    


   /*
    if (Message == "ON") { cmd = "W"; }   // LED on
    if (Message == "OFF") { cmd = "w"; } // LED off
    if (Message == "F") { cmd = "F"; }   // Move forward
    if (Message == "B") { cmd = "B";} // Move backward
    if (Message == "L") { cmd = "L"; }   // Move left
    if (Message == "R") { cmd = "R"; } // Move right
    if (Message == "S") { cmd = "S"; } // Stop moving
    if (Message == "1") { cmd = "ONE"; } // 
    if (Message == "2") { cmd = "TWO"; } //
   */


    
    //Serial.println();
    //Serial.println("-----------------------");
}


String temp;
const char*c_temp = temp.c_str();




///////////////////////////////////////////////////////////初始化定义/////////////////////////////////////////////////////////////////

void setup() {
  Serial.begin(115200);         // set up seriamonitor at 115200 bps 初始化串口                                
  delay(100);                                           //这个延时是为了让我打开串口助手
  setupWifi();                                           //调用函数连接WIFI
  client.setServer(mqtt_server, port);                   //设置客户端连接的服务器,连接Onenet服务器, 使用1883端口
  client.connect(mqtt_devid, mqtt_pubid, mqtt_password); //客户端连接到指定的产品的指定设备.同时输入鉴权信息
  if (client.connected())
  {
    //Serial.println("cloudnet is connected!");//判断以下是不是连好了
  }
  client.setCallback(callback);                                //设置好客户端收到信息是的回调
  client.subscribe("APP"); //订阅命令下发主题
  tim1.attach(15, sendTempAndHumi);                            //定时每10秒调用一次发送数据函数sendTempAndHumi
  Serial.setDebugOutput(true);
  //Serial.println();
  //Serial.println("*esp32 camera remote control - l293d bluino shield*");
  //Serial.println("--------------------------------------------------------");
  Serial.setDebugOutput(true);
  
  // Set all the motor control pin to Output
  pinMode(DRV_A, OUTPUT);
  pinMode(DRV_B, OUTPUT);
  pinMode(DIR_A, OUTPUT);
  pinMode(DIR_B, OUTPUT);
  
  pinMode(ledPin, OUTPUT); // set the LED pin as an Output
  pinMode(buzzerPin, OUTPUT); // set the buzzer pin as an Output
  pinMode(servoPin, OUTPUT); // set the servo pin as an Output

  // Initial state - turn off motors, LED & buzzer
  digitalWrite(DRV_A, LOW);
  digitalWrite(DRV_B, LOW);
  digitalWrite(DIR_A, LOW);
  digitalWrite(DIR_B, LOW);
  digitalWrite(ledPin, LOW);
  digitalWrite(buzzerPin, LOW);
  digitalWrite(servoPin, LOW);

  /* Initializing Servo and LED */
  initServo();
  initLed();
  init_moter();
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  
  //init with high specs to pre-allocate larger buffers
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

#if defined(CAMERA_MODEL_ESP_EYE)
  pinMode(13, INPUT_PULLUP);
  pinMode(14, INPUT_PULLUP);
#endif

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("camera init failed with error 0x%x", err);
    return;
  }

  sensor_t * s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1); // flip it back
    s->set_brightness(s, 1); // up the brightness just a bit
    s->set_saturation(s, -2); // lower the saturation
  }
  // drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_QVGA);

#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
  s->set_vflip(s, 1);
  s->set_hmirror(s, 1);
#endif

  startCameraServer();
  Serial.print("camera ready! use 'http://");
  Serial.print(WiFi.localIP());
  0Serial.println("' to connect");
  
  
  IPAddress ip;
  
  
  
  ip = WiFi.localIP();
  
  
  
  Serial.println(F("wifi connected"));
  Serial.println("");
  Serial.print("stream link: http://");
  Serial.print(ip);
  Serial.println("/mjpeg/1");

 /* extern std::string ip2 = "";
  ip2 = String(WiFi.localIP());
  
  const char* ipStr = ip2;
  byte ip3[4] = ip2;
  parseBytes (ipStr, '.', ip3, 4, 10); 
  client.publish("ESP32HTTP", ip3); 
  */

  /*ipadd1 = WiFi.localIP().toString().c_str();
  */ 
  

  ArduinoOTA.begin();   // enable to receive update/upload firmware via Wifi OTA


  // Start mainstreaming RTOS task
  xTaskCreatePinnedToCore(
    mjpegCB,
    "mjpeg",
    4 * 1024,
    NULL,
    2,
    &tMjpeg,
    APP_CPU);



}









//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


















/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";

httpd_handle_t stream_httpd = NULL;
httpd_handle_t camera_httpd = NULL;


static size_t jpg_encode_stream(void * arg, size_t index, const void* data, size_t len){
    jpg_chunking_t *j = (jpg_chunking_t *)arg;
    if(!index){
        j->len = 0;
    }
    if(httpd_resp_send_chunk(j->req, (const char *)data, len) != ESP_OK){
        return 0;
    }
    j->len += len;
    return len;
}

static esp_err_t capture_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL;
    esp_err_t res = ESP_OK;
    int64_t fr_start = esp_timer_get_time();

    fb = esp_camera_fb_get();
    if (!fb) {
        Serial.printf("Camera capture failed");
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }

    httpd_resp_set_type(req, "image/jpeg");
    httpd_resp_set_hdr(req, "Content-Disposition", "inline; filename=capture.jpg");

    size_t fb_len = 0;
    if(fb->format == PIXFORMAT_JPEG){
        fb_len = fb->len;
        res = httpd_resp_send(req, (const char *)fb->buf, fb->len);
    } else {
        jpg_chunking_t jchunk = {req, 0};
        res = frame2jpg_cb(fb, 80, jpg_encode_stream, &jchunk)?ESP_OK:ESP_FAIL;
        httpd_resp_send_chunk(req, NULL, 0);
        fb_len = jchunk.len;
    }
    esp_camera_fb_return(fb);
    int64_t fr_end = esp_timer_get_time();
    Serial.printf("JPG: %uB %ums", (uint32_t)(fb_len), (uint32_t)((fr_end - fr_start)/1000));
    return res;
}

static esp_err_t stream_handler(httpd_req_t *req){
    camera_fb_t * fb = NULL;
    esp_err_t res = ESP_OK;
    size_t _jpg_buf_len = 0;
    uint8_t * _jpg_buf = NULL;
    char * part_buf[64];

    static int64_t last_frame = 0;
    if(!last_frame) {
        last_frame = esp_timer_get_time();
    }

    res = httpd_resp_set_type(req, _STREAM_CONTENT_TYPE);
    if(res != ESP_OK){
        return res;
    }

    while(true){
        fb = esp_camera_fb_get();
        if (!fb) {
            Serial.printf("Camera capture failed");
            res = ESP_FAIL;
        } else {
            if(fb->format != PIXFORMAT_JPEG){
                bool jpeg_converted = frame2jpg(fb, 80, &_jpg_buf, &_jpg_buf_len);
                esp_camera_fb_return(fb);
                fb = NULL;
                if(!jpeg_converted){
                    Serial.printf("JPEG compression failed");
                    res = ESP_FAIL;
                }
            } else {
                _jpg_buf_len = fb->len;
                _jpg_buf = fb->buf;
            }
        }
        if(res == ESP_OK){
            size_t hlen = snprintf((char *)part_buf, 64, _STREAM_PART, _jpg_buf_len);
            res = httpd_resp_send_chunk(req, (const char *)part_buf, hlen);
        }
        if(res == ESP_OK){
            res = httpd_resp_send_chunk(req, (const char *)_jpg_buf, _jpg_buf_len);
        }
        if(res == ESP_OK){
            res = httpd_resp_send_chunk(req, _STREAM_BOUNDARY, strlen(_STREAM_BOUNDARY));
        }
        if(fb){
            esp_camera_fb_return(fb);
            fb = NULL;
            _jpg_buf = NULL;
        } else if(_jpg_buf){
            free(_jpg_buf);
            _jpg_buf = NULL;
        }
        if(res != ESP_OK){
            break;
        }
        int64_t fr_end = esp_timer_get_time();

        int64_t frame_time = fr_end - last_frame;
        last_frame = fr_end;
        frame_time /= 1000;
    }

    last_frame = 0;
    return res;
}


static esp_err_t cmd_handler(httpd_req_t *req){
    char*  buf;
    size_t buf_len;
    char variable[32] = {0,};
    char value[32] = {0,};

    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = (char*)malloc(buf_len);
        Serial.println(buf);
        if(!buf){
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
        if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            if (httpd_query_key_value(buf, "var", variable, sizeof(variable)) == ESP_OK &&
                httpd_query_key_value(buf, "val", value, sizeof(value)) == ESP_OK) {
            } else {
                free(buf);
                Serial.println(buf);
                httpd_resp_send_404(req);
                return ESP_FAIL;
            }
        } else {
            free(buf);
            Serial.println(buf);
            httpd_resp_send_404(req);
            return ESP_FAIL;
        }
        Serial.println(buf);
        free(buf);
    } else {
        httpd_resp_send_404(req);
        Serial.println(ESP_FAIL);
        return ESP_FAIL;
    }

    int val = atoi(value);
    sensor_t * s = esp_camera_sensor_get();
    int res = 0;

    if(!strcmp(variable, "framesize")) {
        if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val);
    }
    else if(!strcmp(variable, "quality")) res = s->set_quality(s, val);
    else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val);
    else if(!strcmp(variable, "brightness")) res = s->set_brightness(s, val);
    else if(!strcmp(variable, "saturation")) res = s->set_saturation(s, val);
    else if(!strcmp(variable, "gainceiling")) res = s->set_gainceiling(s, (gainceiling_t)val);
    else if(!strcmp(variable, "colorbar")) res = s->set_colorbar(s, val);
    else if(!strcmp(variable, "awb")) res = s->set_whitebal(s, val);
    else if(!strcmp(variable, "agc")) res = s->set_gain_ctrl(s, val);
    else if(!strcmp(variable, "aec")) res = s->set_exposure_ctrl(s, val);
    else if(!strcmp(variable, "hmirror")) res = s->set_hmirror(s, val);
    else if(!strcmp(variable, "vflip")) res = s->set_vflip(s, val);
    else if(!strcmp(variable, "awb_gain")) res = s->set_awb_gain(s, val);
    else if(!strcmp(variable, "agc_gain")) res = s->set_agc_gain(s, val);
    else if(!strcmp(variable, "aec_value")) res = s->set_aec_value(s, val);
    else if(!strcmp(variable, "aec2")) res = s->set_aec2(s, val);
    else if(!strcmp(variable, "dcw")) res = s->set_dcw(s, val);
    else if(!strcmp(variable, "bpc")) res = s->set_bpc(s, val);
    else if(!strcmp(variable, "wpc")) res = s->set_wpc(s, val);
    else if(!strcmp(variable, "raw_gma")) res = s->set_raw_gma(s, val);
    else if(!strcmp(variable, "lenc")) res = s->set_lenc(s, val);
    else if(!strcmp(variable, "special_effect")) res = s->set_special_effect(s, val);
    else if(!strcmp(variable, "wb_mode")) res = s->set_wb_mode(s, val);
    else if(!strcmp(variable, "ae_level")) res = s->set_ae_level(s, val);
    else {
        res = -1;
    }

    if(res){
        return httpd_resp_send_500(req);
    }

    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    return httpd_resp_send(req, NULL, 0);
}

static esp_err_t status_handler(httpd_req_t *req){
    static char json_response[1024];

    sensor_t * s = esp_camera_sensor_get();
    char * p = json_response;
    *p++ = '{';

    p+=sprintf(p, "\"framesize\":%u,", s->status.framesize);
    p+=sprintf(p, "\"quality\":%u,", s->status.quality);
    p+=sprintf(p, "\"brightness\":%d,", s->status.brightness);
    p+=sprintf(p, "\"contrast\":%d,", s->status.contrast);
    p+=sprintf(p, "\"saturation\":%d,", s->status.saturation);
    p+=sprintf(p, "\"special_effect\":%u,", s->status.special_effect);
    p+=sprintf(p, "\"wb_mode\":%u,", s->status.wb_mode);
    p+=sprintf(p, "\"awb\":%u,", s->status.awb);
    p+=sprintf(p, "\"awb_gain\":%u,", s->status.awb_gain);
    p+=sprintf(p, "\"aec\":%u,", s->status.aec);
    p+=sprintf(p, "\"aec2\":%u,", s->status.aec2);
    p+=sprintf(p, "\"ae_level\":%d,", s->status.ae_level);
    p+=sprintf(p, "\"aec_value\":%u,", s->status.aec_value);
    p+=sprintf(p, "\"agc\":%u,", s->status.agc);
    p+=sprintf(p, "\"agc_gain\":%u,", s->status.agc_gain);
    p+=sprintf(p, "\"gainceiling\":%u,", s->status.gainceiling);
    p+=sprintf(p, "\"bpc\":%u,", s->status.bpc);
    p+=sprintf(p, "\"wpc\":%u,", s->status.wpc);
    p+=sprintf(p, "\"raw_gma\":%u,", s->status.raw_gma);
    p+=sprintf(p, "\"lenc\":%u,", s->status.lenc);
    p+=sprintf(p, "\"hmirror\":%u,", s->status.hmirror);
    p+=sprintf(p, "\"dcw\":%u,", s->status.dcw);
    p+=sprintf(p, "\"colorbar\":%u", s->status.colorbar);
    *p++ = '}';
    *p++ = 0;
    httpd_resp_set_type(req, "application/json");
    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    return httpd_resp_send(req, json_response, strlen(json_response));
}


static esp_err_t state_handler(httpd_req_t *req){
    char*  buf;
    size_t buf_len;
    char cmd[32] = {0,};
    char *topic;
    byte *payload;
    unsigned int length;
    size_t i = 0;
    char receivedChar = (char)payload[i];
    
    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = (char*)malloc(buf_len);
        //Serial.println(buf);  
        if(!buf){
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
        
        if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            if (httpd_query_key_value(buf, "cmd", cmd, sizeof(cmd)) == ESP_OK) {
             
            } else {
                free(buf);
                Serial.print("*");
                Serial.println(ESP_FAIL);
                httpd_resp_send_404(req);
                return ESP_FAIL;
            }
        } else {
            free(buf);
            Serial.print("**");
            Serial.println(ESP_FAIL);
            httpd_resp_send_404(req);
            return ESP_FAIL;
        }
        free(buf);
        
    } else {
        Serial.print("***");
        Serial.println(ESP_FAIL);
        httpd_resp_send_404(req);
        return ESP_FAIL;
    }

    int res = 0;
    ///////////命令下发
    
    if(!strcmp(cmd,"F")) {
      Serial.println("F");
      // digitalWrite(DRV_A, LOW);
      // digitalWrite(DRV_B, HIGH);
      // digitalWrite(DIR_A, LOW);
      // digitalWrite(DIR_B, HIGH);
      ledcWrite(9, 0);
      ledcWrite(10, 255);
      ledcWrite(11, 0);
      ledcWrite(12, 255);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd,"B")) {
      Serial.println("B");
      // digitalWrite(DRV_A, HIGH);
      // digitalWrite(DRV_B, LOW);
      // digitalWrite(DIR_A, HIGH);
      // digitalWrite(DIR_B, LOW);
      ledcWrite(9, 255);
      ledcWrite(10, 0);
      ledcWrite(11, 255);
      ledcWrite(12, 0);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "R")) {
      Serial.println("R");
      // digitalWrite(DRV_A, HIGH);
      // digitalWrite(DRV_B, LOW);
      // digitalWrite(DIR_A, LOW);
      // digitalWrite(DIR_B, HIGH);
      ledcWrite(9, 80);
      ledcWrite(10, 0);
      ledcWrite(11, 0);
      ledcWrite(12, 80);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "L")) {
      Serial.println("L");
      // digitalWrite(DRV_A, LOW);
      // digitalWrite(DRV_B, HIGH);
      // digitalWrite(DIR_A, HIGH);
      // digitalWrite(DIR_B, LOW);
      ledcWrite(9, 0);
      ledcWrite(10, 80);
      ledcWrite(11, 80);
      ledcWrite(12, 0);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    /*else if(!strcmp(cmd, "G")) {
      Serial.println("Forward Left");
      // digitalWrite(DRV_A, LOW);
      // digitalWrite(DRV_B, LOW);
      // digitalWrite(DIR_A, LOW);
      // digitalWrite(DIR_B, HIGH);

      ledcWrite(9, 0);
      ledcWrite(10, 20);
      ledcWrite(11, 0);
      ledcWrite(12, 255);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "H")) {
      Serial.println("Backward Left");
      // digitalWrite(DRV_A, HIGH);
      // digitalWrite(DRV_B, LOW);
      // digitalWrite(DIR_A, LOW);
      // digitalWrite(DIR_B, LOW);
      ledcWrite(9, 255);
      ledcWrite(10, 0);
      ledcWrite(11, 0);
      ledcWrite(12, 0);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "I")) {
      Serial.println("Forward Right");
      // digitalWrite(DRV_A, LOW);
      // digitalWrite(DRV_B, HIGH);
      // digitalWrite(DIR_A, LOW);
      // digitalWrite(DIR_B, LOW);
      ledcWrite(9, 0);
      ledcWrite(10, 255);
      ledcWrite(11, 0);
      ledcWrite(12, 20);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "J")) {
      Serial.println("Backward Right");
      // digitalWrite(DRV_A, LOW);
      // digitalWrite(DRV_B, LOW);
      // digitalWrite(DIR_A, HIGH);
      // digitalWrite(DIR_B, LOW);
      ledcWrite(9, 0);
      ledcWrite(10, 0);
      ledcWrite(11, 255);
      ledcWrite(12, 0);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    */
    else if(!strcmp(cmd, "S")) {
      Serial.println("Stop");
       //digitalWrite(DRV_A, LOW);
       //digitalWrite(DRV_B, LOW);
       //digitalWrite(DIR_A, LOW);
       //digitalWrite(DIR_B, LOW);
      ledcWrite(9, 0);
      ledcWrite(10, 0);
      ledcWrite(11, 0);
      ledcWrite(12, 0);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    /*
    else if(!strcmp(cmd, "V")) {
      Serial.println("Horn On");
      digitalWrite(buzzerPin, HIGH);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "v")) {
      Serial.println("Horn Off");
      digitalWrite(buzzerPin, LOW);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    */
    else if(!strcmp(cmd, "W")) {
      Serial.println("W");
      ledcWrite(7, ledVal);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    
    else if(!strcmp(cmd, "w")) {
      Serial.println("w");
      ledcWrite(7, 0);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    /*
    else if (!strcmp(cmd, "x")){
      Serial.println("Flash Light : Low (20)");
      ledVal = 20;
      ledcWrite(7, ledVal);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    else if (!strcmp(cmd, "y")){
      Serial.println("Flash Light : Medium (50)");
      ledVal = 50;
      ledcWrite(7, ledVal);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    else if (!strcmp(cmd, "z")){
      Serial.println("Flash Light : Bright (100)");
      ledVal = 100;
      ledcWrite(7, ledVal);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }    
    else if (!strcmp(cmd, "Z")){
      Serial.println("Flash Light : Super Bright (255)");
      ledVal = 255;
      ledcWrite(7, ledVal);
      httpd_resp_set_type(req, "text/html");
      return httpd_resp_send(req, "OK", 2);
    }
    */
    else {
        res = -1;
    }

    if(res){
        return httpd_resp_send_500(req);
    }
    
    httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*");
    return httpd_resp_send(req, NULL, 0);
    }

    





 
 

 


///////////////相机服务器定义

void startCameraServer(){
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    

    httpd_uri_t status_uri = {
        .uri       = "/status",
        .method    = HTTP_GET,
        .handler   = status_handler,
        .user_ctx  = NULL
    };

    httpd_uri_t cmd_uri = {
        .uri       = "/control",
        .method    = HTTP_GET,
        .handler   = cmd_handler,
        .user_ctx  = NULL
    };

    httpd_uri_t capture_uri = {
        .uri       = "/capture",
        .method    = HTTP_GET,
        .handler   = capture_handler,
        .user_ctx  = NULL
    };

   httpd_uri_t stream_uri = {
        .uri       = "/stream",
        .method    = HTTP_GET,
        .handler   = stream_handler,
        .user_ctx  = NULL
    };

   httpd_uri_t state_uri = {
        .uri       = "/state",
        .method    = HTTP_GET,
        .handler   = state_handler,
        .user_ctx  = NULL
    };

    Serial.printf("starting web server on port: '%d'", config.server_port);
    if (httpd_start(&camera_httpd, &config) == ESP_OK) {
        httpd_register_uri_handler(camera_httpd, &cmd_uri);
        httpd_register_uri_handler(camera_httpd, &capture_uri);
        httpd_register_uri_handler(camera_httpd, &status_uri);
        httpd_register_uri_handler(camera_httpd, &state_uri);
    }

    config.server_port += 1;
    config.ctrl_port += 1;
    Serial.printf("starting stream server on port: '%d'", config.server_port);
    if (httpd_start(&stream_httpd, &config) == ESP_OK) {
        httpd_register_uri_handler(stream_httpd, &stream_uri);
    }
}















//////循环函数
void loop()
{
  if (!WiFi.isConnected()) //先看WIFI是否还在连接
  {
    setupWifi();
  }
  if (!client.connected()) //如果客户端没连接云服务器, 重新连接
  {
    clientReconnect();
    delay(100);
  }
  client.loop(); //客户端循环检测
  vTaskDelay(1000);
    
    delay(500);
  if(Serial.available()!=0)
  {
  temp=Serial.readString();          
  Serial.println(c_temp); 
  client.publish("ESP32HTTP", c_temp); 
  }
}
